package org.dandsoft.blackbox.snn.storage;

import org.dandsoft.blackbox.snn.NeuronStructure;
import org.dandsoft.blackbox.snn.SnnException;
import org.dandsoft.blackbox.snn.Neuron;
import org.dandsoft.blackbox.snn.Synapse;
import org.dandsoft.blackbox.ObjectId;
import org.dandsoft.blackbox.ObjectIdGenerator;
import org.dandsoft.blackbox.IntegerObjectIdGenerator;

import java.util.Iterator;
import java.util.Hashtable;
import java.util.ArrayList;

/**
 * User: dvilyuzhanin
 * Date: 21.01.2009 15:16:37
 */
public class NeuronStructureInMemory implements NeuronStructure {

    private ObjectIdGenerator generator = new IntegerObjectIdGenerator();
    private Hashtable<ObjectId, NeuronInfo> neurons = new Hashtable<ObjectId, NeuronInfo>();
    private Hashtable<ObjectId, SynapseInfo> synapses = new Hashtable<ObjectId, SynapseInfo>();

    public synchronized int getNeuronsPoolSize() throws SnnException {
        return 0;
    }

    public synchronized void setNeuronsPoolSize(int size) throws SnnException {

    }

    public synchronized int getSynapsePoolSize() throws SnnException {
        return 0;
    }

    public synchronized void setSynapsePoolSize(int size) throws SnnException {

    }

    public synchronized ObjectId createNeuron() throws SnnException {
        ObjectId id = generator.generate();
        Neuron neuron = new Neuron();
        neuron.setId(id);

        NeuronInfo info = new NeuronInfo();
        info.setNeuron(neuron);

        neurons.put(id, info);
        return id;
    }

    public synchronized void deleteNeuron(ObjectId neuronId) throws SnnException {
        neurons.remove(neuronId);
    }

    public synchronized Neuron getNeuron(ObjectId neuron) throws SnnException {
        NeuronInfo info = neurons.get(neuron);
        if (info != null) {
            return info.getNeuron();
        }
        return null;
    }

    public synchronized ObjectId connectNeurons(ObjectId sourceId, ObjectId destinationId) throws SnnException {
        NeuronInfo sourceInfo = neurons.get(sourceId);
        NeuronInfo destinationInfo = neurons.get(destinationId);

        if (sourceInfo == null || destinationInfo == null) {
            return null;
        } else {
            if (sourceInfo.getOutputSynapses().containsKey(destinationId)) {
                return null;
            }

            ObjectId synapseId = generator.generate();
            Synapse synapse = new Synapse();
            synapse.setSource(sourceId);
            synapse.setDestination(destinationId);
            synapse.setId(synapseId);
            SynapseInfo synapseInfo = new SynapseInfo();
            synapseInfo.setSynapse(synapse);

            sourceInfo.getOutputSynapses().put(destinationId, synapseId);
            destinationInfo.getInputSynapses().put(sourceId, synapseId);

            synapses.put(synapseId, synapseInfo);
            return synapseId;
        }
    }

    public synchronized void disconnectNeurons(ObjectId synapseId) throws SnnException {
        SynapseInfo synapseInfo = synapses.get(synapseId);
        if (synapseInfo != null) {
            NeuronInfo sourceInfo = neurons.get(synapseInfo.getSynapse().getSource());
            if (sourceInfo == null) {
                return;
            }

            NeuronInfo destinationInfo = neurons.get(synapseInfo.getSynapse().getDestination());
            if (destinationInfo != null) {
                return;
            }

            sourceInfo.getOutputSynapses().remove(destinationInfo.getNeuron().getId());
            sourceInfo.getInputSynapses().remove(sourceInfo.getNeuron().getId());

            synapses.remove(synapseId);
        }
    }

    public synchronized Synapse getSynapse(ObjectId synapse) throws SnnException {
        SynapseInfo info = synapses.get(synapse);
        if (info != null) {
            return info.getSynapse();
        } else {
            return null;
        }
    }


    public synchronized Iterator<Synapse> getSynapsesBySource(ObjectId sourceId) throws SnnException {
        NeuronInfo neuronInfo = neurons.get(sourceId);
        if (neuronInfo != null) {
            return new SynapsesIterator(neuronInfo.getOutputSynapses().values().iterator());
        }
        return null;
    }

    class SynapsesIterator implements Iterator<Synapse> {
        private Iterator<ObjectId> objectIdIterator;

        SynapsesIterator(Iterator<ObjectId> objectIdIterator) {
            this.objectIdIterator = objectIdIterator;
        }

        public boolean hasNext() {
            return objectIdIterator.hasNext();
        }

        public Synapse next() {
            try {
                return getSynapse(objectIdIterator.next());
            } catch (SnnException e) {
                e.printStackTrace();
                return null;
            }
        }

        public void remove() {

        }
    }

    class NeuronInfo {
        private Neuron neuron;
        private Hashtable<ObjectId, ObjectId> inputSynapses = new Hashtable<ObjectId, ObjectId>();
        private Hashtable<ObjectId, ObjectId> outputSynapses = new Hashtable<ObjectId, ObjectId>();

        public Neuron getNeuron() {
            return neuron;
        }

        public void setNeuron(Neuron neuron) {
            this.neuron = neuron;
        }

        public Hashtable<ObjectId, ObjectId> getInputSynapses() {
            return inputSynapses;
        }

        public void setInputSynapses(Hashtable<ObjectId, ObjectId> inputSynapses) {
            this.inputSynapses = inputSynapses;
        }

        public Hashtable<ObjectId, ObjectId> getOutputSynapses() {
            return outputSynapses;
        }

        public void setOutputSynapses(Hashtable<ObjectId, ObjectId> outputSynapses) {
            this.outputSynapses = outputSynapses;
        }
    }

    class SynapseInfo {
        private Synapse synapse;

        public Synapse getSynapse() {
            return synapse;
        }

        public void setSynapse(Synapse synapse) {
            this.synapse = synapse;
        }
    }

    public ObjectIdGenerator getGenerator() {
        return generator;
    }

    public void setGenerator(ObjectIdGenerator generator) {
        this.generator = generator;
    }
}
